package model

import (
	"config-client-v1/init/global"
	j_log "config-client-v1/init/j-log"
	j_redis "config-client-v1/init/j-redis"
	"config-client-v1/utils/tools"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gorilla/websocket"
	"gorm.io/gorm"
	"sync"
	"time"
)

const ByteLENGTH = 8192
const MaxConnectTimes = 100000

type QueueValue struct {
	ProjectID    int        `json:"project_id"`
	SpecType     string     `json:"spec_type"`                                   // 变量类型
	RealSpecType string     `json:"real_spec_type" gorm:"column:real_spec_type"` // 真类型 (用于存储前强制转换)
	Addr         string     `json:"addr"`                                        // 变量地址
	Alert        string     `json:"alert"`                                       // 报警文字内容描述, 根据报警类型 alert_type 规则生成
	AlertType    *AlertType `json:"alert_type"`                                  // 报警类型
	FieldName    string     `json:"field_name"`                                  // 变量名
	Value        float64    `json:"value"`                                       // 变量值
	Fe           uint8      `gorm:"column:fe" json:"fe"`                         // 是否科学计数法 [0.否 1.是]
	Precision    uint8      `gorm:"column:precision" json:"precision"`           // 小数点保留几位
	FSShield     uint8      `json:"fs_shield"`                                   // 是否屏蔽, 自研设备AI和第三方设备 [0.解除屏蔽 1.屏蔽]
	FSAlert      uint8      `json:"fs_alert"`                                    // 自研设备报警,服务端专用 [11.正常 12.上限报警 13.上上限报警 14.下限报警 15.下下限报警]
	CreateTime   *string    `json:"create_time"`                                 //
}

type QueueValues []*QueueValue

var RunningProject *Project           // 当前工程
var RunningPPEMap *RunningPPEMapStu   // 运行中的第三方设备
var RunningFSMap *RunningFSMapStu     // 运行中的自研设备
var TempFSMap *RunningFSMapStu        // 上下载临时用
var VariablesMap *VariablesMapStu     // 全局变量池
var AlertTypeMap *AlertTypeMapStu     // 报警类型池
var AlertDataMap *AlertDataMapStu     // 报警记录池
var OPCWSClient *websocket.Conn       // OPC WS Client
var RCFormulaMap map[int]*RCFormula   // 报警类型池
var ExpAlarmItems *ExpAlarmItemsStu   // 实验报警MAP
var ModbusValueMap *ModbusValueMapStu // modbus变量池MAP
var ImportingProjectExt *ProjectExt   // 正在导入的工程信息

var AllowThirdCode1 = map[string]string{
	"CAN2.0B":       "51",
	"RS485":         "52",
	"RS232":         "53",
	"MODBUS TCP":    "54",
	"MODBUS RTU":    "55",
	"TCP/IP":        "56",
	"PROFINET":      "57",
	"OPC Client":    "58",
	"OPC Server":    "59",
	"PLC":           "60",
	"OPC DA Client": "61",
	"OPC DA Server": "62",
}

var ProtocolAlertConst = map[string]string{
	"CAN2.0B":       "ALERT_CAN20B",
	"RS485":         "ALERT_RS485",
	"RS232":         "ALERT_RS232",
	"MODBUS TCP":    "ALERT_MODBUS_TCP",
	"MODBUS RTU":    "ALERT_MODBUS_RTU",
	"TCP/IP":        "ALERT_TCP_IP",
	"PROFINET":      "ALERT_PROFINET",
	"OPC Client":    "ALERT_OPC_CLIENT",
	"OPC Server":    "ALERT_OPC_SERVER",
	"PLC":           "ALERT_PLC",
	"OPC DA Client": "ALERT_OPC_DA_CLIENT",
	"OPC DA Server": "ALERT_OPC_DA_SERVER",
}

var AllowFSCode2 = map[string]string{
	"AI":     "00",
	"AQ":     "01",
	"DI":     "02",
	"DQ":     "03",
	"Other":  "04",
	"泵组电磁阀":  "05",
	"增压泵电磁阀": "06",
	"电调阀":    "07",
	"压力电磁阀":  "08",
	"电源控制":   "09",
	"电源状态":   "10", // 预留1被占用
	"预留2":    "11",
	"预留3":    "12",
	"模态采集":   "13",
	"故障诊断":   "14",
}

var AllowFSCode2ModuleType = map[string]string{
	"00": "AI",
	"01": "AQ",
	"02": "DI",
	"03": "DQ",
	"04": "Other",
	"05": "泵组电磁阀",
	"06": "增压泵电磁阀",
	"07": "电调阀",
	"08": "压力电磁阀",
	"09": "电源控制",
	"10": "电源状态", // 预留1被占用
	"11": "预留2",
	"12": "预留3",
	"13": "模态采集",
	"14": "故障诊断",
}
var AllowModuleCode2 = map[string]string{
	"AI":    "00",
	"AQ":    "01",
	"DI":    "02",
	"DQ":    "03",
	"Other": "04",
}
var AllowChannelValue = map[int]bool{
	8:  true,
	16: true,
	32: true,
}
var AllowModelCode2 = map[string]string{
	"泵组电磁阀":  "05",
	"增压泵电磁阀": "06",
	"电调阀":    "07",
	"压力电磁阀":  "08",
	"电源控制":   "09",
	"电源状态":   "10",
	"模态采集":   "13",
	"故障诊断":   "14",
}
var AllowProtocol = map[string]bool{
	"RS485":      true,
	"RS232":      true,
	"MODBUS RTU": true,
	"MODBUS TCP": true,
	"TCP/IP":     true,
	"PROFINET":   true,
	"OPC Client": true,
	"OPC Server": true,
	"PLC":        true,
}
var AllowCrcType = map[string]bool{
	"crc16": true,
	"sum":   true,
}
var AllowEndian = map[string]bool{
	"big":    true,
	"little": true,
}
var AllowSpecType = map[string]bool{
	"uint8":   true,
	"int8":    true,
	"uint16":  true,
	"int16":   true,
	"uint32":  true,
	"int32":   true,
	"float32": true,
	"bool":    true,
	"uint64":  true,
	"int64":   true,
	"float64": true,
	"string":  true,
}
var AllowFrameType = map[string]string{
	"send":  "00",
	"recv":  "01",
	"read":  "02",
	"write": "03",
}
var AllowFrameTypeRS485 = map[string]string{
	"send": "00",
	"recv": "01",
}
var AllowFrameTypeModbus = map[string]string{
	"read":  "02",
	"write": "03",
}
var AllowBoolean = map[uint8]bool{
	0: true,
	1: true,
}

var AllowCmd0604Code = map[string]byte{
	"AI":     0x00,
	"AQ":     0x01,
	"DQ":     0x02,
	"Other":  0x03,
	"泵组电磁阀":  0x04,
	"增压泵电磁阀": 0x05,
	"压力电磁阀":  0x06,
}
var AllowPLCType = map[uint8]string{
	1: "西门子",
	2: "AB",
	3: "汇川",
	4: "信捷",
	5: "和利时",
	6: "三菱",
	7: "欧姆龙",
}
var AllowPLCTypeModel = map[string]uint8{
	"S7-1200": 1,
	"S7-1500": 1,
	//"AB":      2,
	"汇川": 3,
	//"信捷":      4,
	//"和利时":     5,
	//"三菱":      6,
	//"欧姆龙":     7,
}

func InitModelData() {
	RunningProject = &Project{}
	RunningPPEMap = &RunningPPEMapStu{}
	RunningPPEMap.M = make(map[int]*PPE)
	RunningFSMap = &RunningFSMapStu{}
	RunningFSMap.M = make(map[int]*FSEquip)
	VariablesMap = &VariablesMapStu{}
	VariablesMap.M = make(map[string]*Variable)
	TempFSMap = &RunningFSMapStu{}
	TempFSMap.M = make(map[int]*FSEquip)
	AlertTypeMap = &AlertTypeMapStu{}
	AlertTypeMap.M = make(map[string]*AlertType)
	AlertDataMap = &AlertDataMapStu{}
	AlertDataMap.M = make(map[string]*VarAlert)

	ModbusValueMap = &ModbusValueMapStu{}
	ModbusValueMap.M = make(map[string][]*ModbusPointBind)

	RCFormulaMap, _ = GetRCFormulaMap()
	go OPCWSClientRun()
}

/*RunningFSMapStu --------------------*/
type RunningFSMapStu struct {
	M map[int]*FSEquip
	sync.RWMutex
}

func (m *RunningFSMapStu) Delete(key int) {
	m.Lock()
	defer m.Unlock()
	delete(m.M, key)
}

func (m *RunningFSMapStu) Get(key int) (v *FSEquip, ok bool) {
	m.RLock()
	defer m.RUnlock()
	v, ok = m.M[key]
	return
}

func (m *RunningFSMapStu) Set(key int, v *FSEquip) {
	m.Lock()
	defer m.Unlock()
	m.M[key] = v
}

func (m *RunningFSMapStu) UpdateDS(key int, ds *DataScale, t string) {
	m.Lock()
	defer m.Unlock()
	v, ok := m.M[key]
	if !ok {
		tmp := &FSEquip{
			ID: key,
		}
		tmp.DS = ds
		if t == "module" {
			tmp.DSModuleRead = time.Now().Unix()
		} else {
			tmp.DSModelRead = time.Now().Unix()
		}
		m.M[key] = tmp
	} else {
		if t == "module" {
			v.DS.AI = ds.AI
			v.DS.AQ = ds.AQ
			v.DS.DI = ds.DI
			v.DS.DQ = ds.DQ
			v.DS.Other = ds.Other
			v.DSModuleRead = time.Now().Unix()
		} else {
			v.DS.Bengzu = ds.Bengzu
			v.DS.Zengya = ds.Zengya
			v.DS.Diantiao = ds.Diantiao
			v.DS.Pressure = ds.Pressure
			v.DS.Power = ds.Power
			v.DS.PowerState = ds.PowerState
			v.DS.ModeCollect = ds.ModeCollect
			v.DS.Diag = ds.Diag
			v.DSModelRead = time.Now().Unix()
		}
	}
}

func (m *RunningFSMapStu) UpdateModule(key int, moduleType, code2, code3 string, frameItem []*FrameItem) {
	m.Lock()
	defer m.Unlock()
	v, ok := m.M[key]
	if ok {
		found := false
		for _, module := range v.FSModule {
			if module.Code2 == code2 && module.Code3 == code3 {
				found = true
				module.FrameItem = frameItem
			}
		}
		if !found {
			now := tools.GetNow()
			tmpModule := &FSModule{
				FSID:       key,
				ModuleType: moduleType,
				ModuleName: moduleType + code3,
				Code1:      v.Code1,
				Code2:      code2,
				Code3:      code3,
				FrameItem:  frameItem,
				CreateTime: &now,
				UpdateTime: &now,
			}
			v.FSModule = append(v.FSModule, tmpModule)
			// 排序
		}
	}
}

func (m *RunningFSMapStu) UpdateModel(key int, moduleType, code2, code3 string, content interface{}) {
	jsonB, _ := json.Marshal(content)
	m.Lock()
	defer m.Unlock()
	v, ok := m.M[key]
	if ok {
		found := false
		for _, module := range v.FSModule {
			if module.Code2 == code2 && module.Code3 == code3 {
				found = true
				module.JsonDetail = string(jsonB)
			}
		}
		if !found {
			now := tools.GetNow()
			tmpModule := &FSModule{
				FSID:       key,
				ModuleType: moduleType,
				ModuleName: moduleType + code3,
				Code1:      v.Code1,
				Code2:      code2,
				Code3:      code3,
				JsonDetail: string(jsonB),
				CreateTime: &now,
				UpdateTime: &now,
			}
			v.FSModule = append(v.FSModule, tmpModule)
			// 排序
		}
	}

}

func IPReadHandle(fse *FSEquip, data *FSEquip) {
	fse.IP = data.IP
	fse.Port = data.Port
	fse.RedundantEnable = data.RedundantEnable
	fse.RedundantIP = data.RedundantIP
	fse.RedundantPort = data.RedundantPort
	fse.RedundantParam1 = data.RedundantParam1
	fse.RedundantParam2 = data.RedundantParam2
	fse.RedundantParam3 = data.RedundantParam3

	tmpFSE, ok := TempFSMap.Get(fse.ID)
	if ok {
		tmpFSE.IP = data.IP
		tmpFSE.Port = data.Port
		tmpFSE.RedundantEnable = data.RedundantEnable
		tmpFSE.RedundantIP = data.RedundantIP
		tmpFSE.RedundantPort = data.RedundantPort
		tmpFSE.RedundantParam1 = data.RedundantParam1
		tmpFSE.RedundantParam2 = data.RedundantParam2
		tmpFSE.RedundantParam3 = data.RedundantParam3
	}

	uploadData := &UploadHistory{
		ProjectID:    RunningProject.ID,
		FSID:         fse.ID,
		ModuleType:   "IP_READ",
		ModuleNumber: 0,
		Status:       3,
	}

	err := DB.Model(&FSEquip{}).Where("id = ?", fse.ID).Updates(map[string]interface{}{
		"ip":               data.IP,
		"port":             data.Port,
		"redundant_enable": data.RedundantEnable,
		"redundant_ip":     data.RedundantIP,
		"redundant_port":   data.RedundantPort,
		"redundant_param1": data.RedundantParam1,
		"redundant_param2": data.RedundantParam2,
		"redundant_param3": data.RedundantParam3,
	}).Error
	if err != nil {
		j_log.LogError([]interface{}{"IPReadHandle error:", err})
	}
	jsonB, _ := json.Marshal(&data)
	uploadData.Data = string(jsonB)
	err = uploadData.Insert()
	if err != nil {
		j_log.LogError([]interface{}{"IPReadHandle error:", err})
	}
}

// ModuleUploadHandle FS上载处理
func ModuleUploadHandle(fse *FSEquip, moduleType string, moduleNumber uint8, code2, code3 string, dataPart []*FrameItem) {
	for _, item := range dataPart {
		item.FSID = fse.ID
		switch item.ChannelType {
		case "AI":
			fallthrough
		case "AQ":
			item.SpecType = "float32"
			item.AlertVarAddr = item.Addr + ".alert"
			item.ShieldVarAddr = item.Addr + ".shield"
			item.Upper2LimitVarAddr = item.Addr + ".upper2"
			item.UpperLimitVarAddr = item.Addr + ".upper"
			item.LowerLimitVarAddr = item.Addr + ".lower"
			item.Lower2LimitVarAddr = item.Addr + ".lower2"
		case "DI":
			fallthrough
		case "DQ":
			item.SpecType = "uint8"
		}
	}
	//TempFSMap.UpdateModule(fse.ID, moduleType, code2, code3, dataPart)
	RunningFSMap.UpdateModule(fse.ID, moduleType, code2, code3, dataPart)
	uploadData := &UploadHistory{
		ProjectID:    RunningProject.ID,
		FSID:         fse.ID,
		ModuleType:   moduleType,
		ModuleNumber: int(moduleNumber),
		Status:       1,
	}
	jsonB, _ := json.Marshal(&dataPart)
	uploadData.Data = string(jsonB)
	err := uploadData.Insert()
	if err != nil {
		j_log.LogError([]interface{}{"uploadData.Insert error:", err})
		return
	}
	list, extendList, err := UploadHistoryHandleExecute(fse, uploadData)
	if err != nil {
		j_log.LogError([]interface{}{"UploadHistoryHandleExecute error:", err})
	}
	for _, row := range list {
		VariablesMap.Set(row.Addr, &Variable{
			FrameItem: *row,
		})
	}
	for _, row := range extendList {
		VariablesMap.Set(row.Addr, &Variable{
			FrameItem: *row,
		})
	}
	fse.CalculateDataScale()
}
func ModelUploadHandle(fse *FSEquip, moduleType string, moduleNumber uint8, code2, code3 string, content interface{}) {
	//TempFSMap.UpdateModel(fse.ID, moduleType, code2, code3, content)    // 更新临时内存
	RunningFSMap.UpdateModel(fse.ID, moduleType, code2, code3, content) // 更新运行内存
	uploadData := &UploadHistory{
		ProjectID:    RunningProject.ID,
		FSID:         fse.ID,
		ModuleType:   moduleType,
		ModuleNumber: int(moduleNumber),
		Status:       1,
	}
	jsonB, _ := json.Marshal(&content)
	uploadData.Data = string(jsonB)
	err := uploadData.Insert()
	if err != nil {
		j_log.LogError([]interface{}{"uploadData.Insert error:", err})
		return
	}
	list, extendList, err := UploadHistoryHandleExecute(fse, uploadData)
	if err != nil {
		j_log.LogError([]interface{}{"UploadHistoryHandleExecute error:", err})
	}
	for _, row := range list {
		VariablesMap.Set(row.Addr, &Variable{
			FrameItem: *row,
		})
	}
	for _, row := range extendList {
		VariablesMap.Set(row.Addr, &Variable{
			FrameItem: *row,
		})
	}
	fse.CalculateDataScale()
}

type AlertTypeMapStu struct {
	M map[string]*AlertType
	sync.RWMutex
}

func (m *AlertTypeMapStu) Get(key string) (v *AlertType, ok bool) {
	m.RLock()
	defer m.RUnlock()
	v, ok = m.M[key]
	if !ok {
		v = &AlertType{}
	}
	return
}

type AlertDataMapStu struct {
	M map[string]*VarAlert
	sync.RWMutex
}

func (m *AlertDataMapStu) Set(key string, v *VarAlert) {
	m.Lock()
	defer m.Unlock()
	m.M[key] = v
}
func (m *AlertDataMapStu) Get(key string) (v *VarAlert, ok bool) {
	m.RLock()
	defer m.RUnlock()
	v, ok = m.M[key]
	if !ok {
		v = &VarAlert{}
	}
	return
}
func (m *AlertDataMapStu) Delete(key string) {
	m.Lock()
	defer m.Unlock()
	delete(m.M, key)
}

/*VariablesMapStu --------------------*/
type VariablesMapStu struct {
	M            map[string]*Variable // addr 作为 key
	sync.RWMutex                      // 变量锁
}

func (m *VariablesMapStu) Set(key string, v *Variable) {
	m.Lock()
	defer m.Unlock()
	m.M[key] = v
}
func (m *VariablesMapStu) Get(key string) (v *Variable, ok bool) {
	m.RLock()
	defer m.RUnlock()
	v, ok = m.M[key]
	return
}
func (m *VariablesMapStu) Delete(key string) {
	m.Lock()
	defer m.Unlock()
	delete(m.M, key)
}

// SaveMemData 保存数据到内存, 先公式再系数
func (m *VariablesMapStu) SaveMemData(messageList []*QueueValue) {
	for _, message := range messageList {
		v, ok := m.Get(message.Addr)
		if !ok {
			//j_log.LogError([]interface{}{"WARNING! SaveMemData 未找到变量 Exception", message.Addr})
			continue
		}
		message.RealSpecType = v.RealSpecType
		message.Fe = v.Fe
		message.Precision = v.Precision
		if v.RealSpecType == "bool" {
			v.Value = message.Value
			message.Fe = 0
			continue
		}
		// 1. 量程转换公式
		message.Value = RangeValueConvert(message.Value, v.RCFormulaID)
		// 2. 系数计算
		if v.Ratio != 1.0 {
			if v.RatioOperator == "*" {
				message.Value = message.Value * v.Ratio
			} else {
				if v.Ratio == 0 {
					j_log.LogWarning([]interface{}{fmt.Sprintf("变量[%s][%s]系数的除数为0", v.FieldName, v.Addr)})
					message.Value = 0
				} else {
					message.Value = message.Value / v.Ratio
				}
			}
		}
		// 3. 科学计数法和精度
		if v.Fe != 1 && v.Precision != 99 { // 如果使用科学计数法, 则使用double类型保留原值, 不做处理, 如果不使用科学计数法, 则使用精度裁剪
			message.Value = tools.RoundFloat(message.Value, int(v.Precision))
		}
		v.Value = message.Value
		v.Value = RealSpecTypeConvert(v.Value, v.RealSpecType)
		//if v.Addr == "00-00-01-001" {
		//	fmt.Printf("addr = %s, value=%v, precision=%v\n", v.Addr, message.Value, v.Precision)
		//}
	}
}

func RealSpecTypeConvert(value float64, realSpecType string) (res float64) {
	switch realSpecType {
	case "uint8":
		res = float64(uint8(value))
	case "int8":
		res = float64(int8(value))
	case "uint16":
		res = float64(uint16(value))
	case "int16":
		res = float64(int16(value))
	case "uint32":
		res = float64(uint32(value))
	case "int32":
		res = float64(int32(value))
	case "uint64":
		res = float64(uint64(value))
	case "int64":
		res = float64(int64(value))
	case "float32":
		res = float64(float32(value))
	case "float64":
		res = value
	case "bool":
		if value == 0 {
			res = float64(0)
		} else {
			res = float64(1)
		}
	default:
		res = value
	}
	return
}

// PushMessageToClients 计算上下限报警; 保存历史记录; 并推送消息给界面
func (m *VariablesMapStu) PushMessageToClients(messageList []*QueueValue) {
	m.SaveMemData(messageList)
	AlarmDataList := ComputeExpAlert()
	now := tools.GetNow()
	for _, message := range messageList {
		message.ProjectID = RunningProject.ID
		v, ok := m.Get(message.Addr)
		if !ok {
			//j_log.LogError([]interface{}{"WARNING! PushMessageToClients 未找到变量 Exception", message.Addr})
			continue
		}
		if message.Value > 100000000000 {
			message.Value = 99999999999
		}
		v.Value = message.Value
		message.SpecType = v.SpecType
		message.FieldName = v.FieldName
		message.FSShield = v.Shield

		/*--- 修改Modbus server 点位 ---*/
		err := WriteVariablesToModbusPoint(message.Addr, message.Value)
		if err != nil {
			j_log.LogError([]interface{}{"WriteVariablesToModbusPoint error:", err})
		}
		/*--- 历史数据记录 单条记录 ---*/
		recordData := &VarRecord{
			ProjectID:  RunningProject.ID,
			Addr:       message.Addr,
			FieldName:  v.FieldName,
			SpecType:   v.SpecType,
			Value:      message.Value,
			CreateTime: message.CreateTime,
		}
		VarRecordQueue.Queue <- recordData

		/*--- 报警数据记录 单条记录 ---*/
		alertData := &VarAlert{}
		detail := &AlertDetail{}
		if v.FSID > 0 {
			if global.FSAlert && v.Shield == 11 {
				alertData, detail = FSCheckAlert(message, v)
			}
		} else {
			_, isInExp := ExpAddrMap.Get(message.Addr)
			if !isInExp { // 不属于参数组实验时按原有逻辑判断报警
				alertData, detail = CheckAlert(message, v)
			} else { // 属于参数组实验时,单条报警直接默认无报警
				alertData.ProjectID = RunningProject.ID
				alertData.BaseAddr = message.Addr
				alertData.BaseFieldName = message.FieldName
				alertData.CreateTime = message.CreateTime
				alertData.Alert = ""
			}
		}
		jsonB, _ := json.Marshal(detail)
		alertData.Detail = string(jsonB)
		message.Alert = alertData.Alert
		/*--- 断是否需要记录报警, 是否有需要恢复的报警信息, 是否有屏蔽的报警信息 ---*/
		key := fmt.Sprintf("%s", alertData.BaseAddr)
		if v.Shield == 1 {
			// 判断一下是否屏蔽, 如果屏蔽也当成报警恢复
			alertData.Alert = ""
		}
		lastAlertData, ok2 := AlertDataMap.Get(key)
		if ok2 {
			if alertData.Alert == "" {
				// 内存中有报警信息, 但是当前数据正常
				// 1. 则屏蔽旧报警, 推送新的恢复信息
				//fmt.Printf("内存中有报警信息, 但是当前数据正常, 删除内存中的报警 %s %s\n", alertData.BaseAddr, alertData.Detail)
				alertData.Const = lastAlertData.Const
				alertData.IsRecovery = 1
				alertData.RecoveryTime = &now
				alertData.CreateTime = &now
				AlarmDataList = append(AlarmDataList, alertData)

				lastAlertData.IsRecovery = 1
				lastAlertData.RecoveryTime = &now
				VarAlertQueue.Queue <- lastAlertData

				AlertDataMap.Delete(key)
			} else {
				// 内存中有报警信息, 且当前数据也有报警
				// 1. 报警类型相同, 则忽略
				// 2. 报警类型不同, 则推送屏蔽旧报警, 且推送新报警
				if alertData.Const != lastAlertData.Const {
					// 先把上一次报警恢复
					lastAlertData.IsRecovery = 1
					lastAlertData.RecoveryTime = &now
					VarAlertQueue.Queue <- lastAlertData
					AlarmDataList = append(AlarmDataList, lastAlertData)

					// 再保存并推送本次报警
					alertData.IsRecovery = 0
					alertData.AudioShield = 0
					VarAlertQueue.Queue <- alertData
					AlarmDataList = append(AlarmDataList, alertData)

					AlertDataMap.Set(key, alertData)
				} else {
					//fmt.Printf("内存中有报警信息, 且当前数据也有报警 报警类型一致, 忽略 %s %s\n", alertData.BaseAddr, alertData.Detail)
				}
			}
		} else {
			if alertData.Alert != "" {
				//fmt.Printf("内存中没有报警信息, 此时刚好有报警, 直接记录 %s %s\n", alertData.BaseAddr, alertData.Detail)
				alertData.AudioShield = 0
				alertData.IsRecovery = 0
				VarAlertQueue.Queue <- alertData
				AlarmDataList = append(AlarmDataList, alertData) // 推送报警或恢复
				AlertDataMap.Set(key, alertData)
			}
		}

		// redis 也存一份
		jsonB2, _ := json.Marshal(recordData)
		j_redis.RedisDb.Set(context.Background(), "value:"+recordData.Addr, string(jsonB2), 0)

	}
	/*--- 无条件全部推送 ---*/
	WSClientMap.RLock()
	defer WSClientMap.RUnlock()
	for _, cli := range WSClientMap.M {
		if len(cli.Data) > 90 {
			j_log.LogInfo([]interface{}{"WARNING! channel is nearly full", cli.RemoteAddr})
			_ = cli.Conn.Close()
			cli.CloseSignal <- 1
			continue
		}
		if len(messageList) > 0 {
			cli.Data <- messageList
		}
		if len(AlarmDataList) > 0 {
			cli.AlarmData <- AlarmDataList
		}
	}
}

// 判断是否报警
// message 为接收到的最新变量值
// baseV 为触发报警事件基础变量信息
// 如果 baseV 设置了报警变量, 则 currentValue 为报警变量的当前值
// 如果 baseV 未设置报警变量, 则 currentValue 为 baseV 当前值

type AlertDetail struct {
	AlertAddr        string  `json:"alert_addr"`
	AlertFieldName   string  `json:"alert_field_name"`
	AlertValue       float64 `json:"alert_value"`
	CompareAddr      string  `json:"compare_addr"`
	CompareFieldName string  `json:"compare_field_name"`
	CompareValue     float64 `json:"compare_value"`
}

func FSCheckAlert(message *QueueValue, baseV *Variable) (alertData *VarAlert, detail *AlertDetail) {
	alertData = &VarAlert{}
	detail = &AlertDetail{}
	alertData.ProjectID = RunningProject.ID
	alertData.BaseAddr = baseV.Addr
	alertData.BaseFieldName = baseV.FieldName
	alertData.CreateTime = message.CreateTime
	currentValue := message.Value
	detail.AlertValue = currentValue
	switch message.FSAlert {
	case 11:
		alertData.Alert = ""
	case 12: // 上
		at, _ := AlertTypeMap.Get("ALERT_HIGH_LIMIT_EXCEEDED")
		alertData.Const = at.Const
		alertData.T1 = at.T1
		alertData.T2 = at.T2
		alertData.Audio = at.Audio
		alertData.Alert = fmt.Sprintf("%s%s", baseV.FieldName, at.Desc)
	case 13: // 上上
		at, _ := AlertTypeMap.Get("ALERT_HIGH_HIGH_LIMIT_EXCEEDED")
		alertData.Const = at.Const
		alertData.T1 = at.T1
		alertData.T2 = at.T2
		alertData.Audio = at.Audio
		alertData.Alert = fmt.Sprintf("%s%s", baseV.FieldName, at.Desc)
	case 14: // 下
		at, _ := AlertTypeMap.Get("ALERT_LOW_LIMIT_EXCEEDED")
		alertData.Const = at.Const
		alertData.T1 = at.T1
		alertData.T2 = at.T2
		alertData.Audio = at.Audio
		alertData.Alert = fmt.Sprintf("%s%s", baseV.FieldName, at.Desc)
	case 15: // 下下
		at, _ := AlertTypeMap.Get("ALERT_LOW_LOW_LIMIT_EXCEEDED")
		alertData.Const = at.Const
		alertData.T1 = at.T1
		alertData.T2 = at.T2
		alertData.Audio = at.Audio
		alertData.Alert = fmt.Sprintf("%s%s", baseV.FieldName, at.Desc)
	default:
		j_log.LogWarning([]interface{}{fmt.Sprintf("fs_alert值异常=%d", message.FSAlert)})
		alertData.Alert = ""
	}
	return
}
func CheckAlert(message *QueueValue, baseV *Variable) (alertData *VarAlert, detail *AlertDetail) {
	alertData = &VarAlert{}
	detail = &AlertDetail{}
	alertData.ProjectID = RunningProject.ID
	alertData.BaseAddr = baseV.Addr
	alertData.BaseFieldName = baseV.FieldName
	alertData.CreateTime = message.CreateTime
	currentValue := message.Value
	if baseV.AlertVarAddr != "" { //
		alertV, ok := VariablesMap.Get(baseV.AlertVarAddr)
		if !ok {
			j_log.LogError([]interface{}{"未找到报警变量", baseV.AlertVarAddr})
			return
		}
		currentValue = alertV.Value
		detail.AlertAddr = alertV.Addr
		detail.AlertFieldName = alertV.FieldName
	}
	detail.AlertValue = currentValue

	if baseV.Upper2LimitEnable == 1 { // 如果勾选了上上限报警
		if baseV.Upper2LimitVarAddr != "" { // 如果上上限设置了变量, 是动态值
			compareV, ok := VariablesMap.Get(baseV.Upper2LimitVarAddr)
			//fmt.Printf("[%s]debug --- 上上限 %s;%s;%.2f;%.2f;\n", *message.CreateTime, baseV.Upper2LimitVarAddr, compareV.Addr, currentValue, compareV.Value)
			if ok && currentValue > compareV.Value {
				//alertData.Alert = fmt.Sprintf("%s(%s) 超出上上限,当前值 %.2f,比较值 %.2f", baseV.FieldName, message.Addr, currentValue, compareV.Value)
				detail.CompareAddr = compareV.Addr
				detail.CompareFieldName = compareV.FieldName
				detail.CompareValue = compareV.Value

				at, _ := AlertTypeMap.Get("ALERT_HIGH_HIGH_LIMIT_EXCEEDED")
				alertData.Const = at.Const
				alertData.T1 = at.T1
				alertData.T2 = at.T2
				alertData.Audio = at.Audio
				alertData.Alert = fmt.Sprintf("%s%s", baseV.FieldName, at.Desc)
				return
			}
		} else { // 上上限为静态值
			detail.CompareValue = baseV.Upper2LimitValue
			if currentValue > baseV.Upper2LimitValue {
				//alertData.Alert = fmt.Sprintf("%s(%s) 超出上上限,当前值 %.2f,比较值 %.2f", baseV.FieldName, message.Addr, currentValue, baseV.Upper2LimitValue)

				at, _ := AlertTypeMap.Get("ALERT_HIGH_HIGH_LIMIT_EXCEEDED")
				alertData.Const = at.Const
				alertData.T1 = at.T1
				alertData.T2 = at.T2
				alertData.Audio = at.Audio
				alertData.Alert = fmt.Sprintf("%s%s", baseV.FieldName, at.Desc)
				return
			}
		}

	}

	if baseV.UpperLimitEnable == 1 {
		if baseV.UpperLimitVarAddr != "" {
			compareV, ok := VariablesMap.Get(baseV.UpperLimitVarAddr)
			if ok && currentValue > compareV.Value {
				//alertData.Alert = fmt.Sprintf("%s(%s) 超出上限,当前值 %.2f,比较值 %.2f", baseV.FieldName, message.Addr, currentValue, compareV.Value)
				detail.CompareAddr = compareV.Addr
				detail.CompareFieldName = compareV.FieldName
				detail.CompareValue = compareV.Value

				at, _ := AlertTypeMap.Get("ALERT_HIGH_LIMIT_EXCEEDED")
				alertData.Const = at.Const
				alertData.T1 = at.T1
				alertData.T2 = at.T2
				alertData.Audio = at.Audio
				alertData.Alert = fmt.Sprintf("%s%s", baseV.FieldName, at.Desc)
				return
			}
		} else {
			detail.CompareValue = baseV.UpperLimitValue
			if currentValue > baseV.UpperLimitValue {
				//alertData.Alert = fmt.Sprintf("%s(%s) 超出上限,当前值 %.2f,比较值 %.2f", baseV.FieldName, message.Addr, currentValue, baseV.UpperLimitValue)
				at, _ := AlertTypeMap.Get("ALERT_HIGH_LIMIT_EXCEEDED")
				alertData.Const = at.Const
				alertData.T1 = at.T1
				alertData.T2 = at.T2
				alertData.Audio = at.Audio
				alertData.Alert = fmt.Sprintf("%s%s", baseV.FieldName, at.Desc)
				return
			}
		}
	}

	if baseV.LowerLimitEnable == 1 {
		if baseV.LowerLimitVarAddr != "" {
			compareV, ok := VariablesMap.Get(baseV.LowerLimitVarAddr)
			if ok && currentValue < compareV.Value {
				//alertData.Alert = fmt.Sprintf("%s(%s) 低于下限,当前值 %.2f,比较值 %.2f", baseV.FieldName, message.Addr, currentValue, compareV.Value)
				detail.CompareAddr = compareV.Addr
				detail.CompareFieldName = compareV.FieldName
				detail.CompareValue = compareV.Value

				at, _ := AlertTypeMap.Get("ALERT_LOW_LIMIT_EXCEEDED")
				alertData.Const = at.Const
				alertData.T1 = at.T1
				alertData.T2 = at.T2
				alertData.Audio = at.Audio
				alertData.Alert = fmt.Sprintf("%s%s", baseV.FieldName, at.Desc)
				return
			}
		} else {
			detail.CompareValue = baseV.LowerLimitValue
			if currentValue < baseV.LowerLimitValue {
				//alertData.Alert = fmt.Sprintf("%s(%s) 低于下限,当前值 %.2f,比较值 %.2f", baseV.FieldName, message.Addr, currentValue, baseV.LowerLimitValue)

				at, _ := AlertTypeMap.Get("ALERT_LOW_LIMIT_EXCEEDED")
				alertData.Const = at.Const
				alertData.T1 = at.T1
				alertData.T2 = at.T2
				alertData.Audio = at.Audio
				alertData.Alert = fmt.Sprintf("%s%s", baseV.FieldName, at.Desc)
				return
			}
		}
	}

	if baseV.Lower2LimitEnable == 1 {
		if baseV.Lower2LimitVarAddr != "" {
			compareV, ok := VariablesMap.Get(baseV.Lower2LimitVarAddr)
			if ok && currentValue < compareV.Value {
				//alertData.Alert = fmt.Sprintf("%s(%s) 低于下下限,当前值 %.2f,比较值 %.2f", baseV.FieldName, message.Addr, currentValue, compareV.Value)
				detail.CompareAddr = compareV.Addr
				detail.CompareFieldName = compareV.FieldName
				detail.CompareValue = compareV.Value

				at, _ := AlertTypeMap.Get("ALERT_LOW_LOW_LIMIT_EXCEEDED")
				alertData.Const = at.Const
				alertData.T1 = at.T1
				alertData.T2 = at.T2
				alertData.Audio = at.Audio
				alertData.Alert = fmt.Sprintf("%s%s", baseV.FieldName, at.Desc)
				return
			}
		} else {
			detail.CompareValue = baseV.Lower2LimitValue
			if currentValue < baseV.Lower2LimitValue {
				//alertData.Alert = fmt.Sprintf("%s(%s) 低于下下限,当前值 %.2f,比较值 %.2f", baseV.FieldName, message.Addr, currentValue, baseV.Lower2LimitValue)
				at, _ := AlertTypeMap.Get("ALERT_LOW_LOW_LIMIT_EXCEEDED")
				alertData.Const = at.Const
				alertData.T1 = at.T1
				alertData.T2 = at.T2
				alertData.Audio = at.Audio
				alertData.Alert = fmt.Sprintf("%s%s", baseV.FieldName, at.Desc)
				return
			}
		}
	}
	return
}

/*--- 运行中的第三方设备 ---*/

type RunningPPEMapStu struct {
	M map[int]*PPE
	sync.RWMutex
}

func (m *RunningPPEMapStu) Delete(key int) {
	m.Lock()
	defer m.Unlock()
	delete(m.M, key)
}

func (m *RunningPPEMapStu) Get(key int) (v *PPE, ok bool) {
	m.RLock()
	defer m.RUnlock()
	v, ok = m.M[key]
	return
}

func (m *RunningPPEMapStu) Set(key int, v *PPE) {
	m.Lock()
	defer m.Unlock()
	m.M[key] = v
}

/*--- FS分帧处理-数据结构 ---*/

type FrameCacheMapStu struct {
	M map[string]*FrameCache // key为功能码+命令码, 示例 0602 + 帧序号
	sync.RWMutex
}
type FrameCache struct {
	FrameCount uint8  // 总帧数
	FrameNum   uint8  // 帧序号
	Data       []byte // 帧数据
	CreateTime int64  // 接收时间
}

func (m *FrameCacheMapStu) Set(funcCode, cmdCode, frameNum uint8, v *FrameCache) {
	key := fmt.Sprintf("%02d%02d%02d", funcCode, cmdCode, frameNum)
	m.Lock()
	defer m.Unlock()
	m.M[key] = v
}

// MergeDataPart0602 当接收到最后子帧后合并分帧数据
// 分帧处理逻辑:
// 当接收到的 帧序号不等于总帧数时, 则将当前帧缓存到map中
// 当接收到的 帧序号等于总帧数时, 并将map中的帧序号小于总帧数的帧的数据部分合并到一起, 并返回合并后的数据
func (m *FrameCacheMapStu) MergeDataPart0602(funcCode, cmdCode, frameNum uint8, lastFrameDataPart []byte) (data []byte, err error) {
	data = make([]byte, 0)
	m.RLock()
	defer m.RUnlock()
	for i := 1; i < int(frameNum); i++ {
		key := fmt.Sprintf("%02d%02d%02d", funcCode, cmdCode, i)
		cache, ok := m.M[key]
		if !ok {
			err = errors.New("没有找到对应的帧缓存 " + key)
			return
		}
		// 是否需要验证接收时间, 超出10秒则删除缓存, 并超时提示?
		//fmt.Printf("=== 合并帧 total=%d,num=%d,len=%d\n%s\n", cache.FrameCount, cache.FrameNum, len(cache.Data), tools.ByteArrayToHexStr(cache.Data))
		data = append(data, cache.Data...)
	}
	data = append(data, lastFrameDataPart...)
	return
}

func SyncFSEquipToMem(FSID int) (err error) {
	fse, ok := RunningFSMap.Get(FSID)
	if !ok {
		//err = errors.New("FS设备TCP未连接")
		return
	}
	tmp := &FSEquip{}
	err = DB.Preload("FSModule",
		func(tx *gorm.DB) *gorm.DB {
			return tx.Preload("FrameItem").Order("code2 ASC,code3 ASC")
		}).
		Where("id = ?", FSID).Limit(1).Find(tmp).Error
	if err != nil {
		return
	}
	// 更新基本信息
	fse.Name = tmp.Name
	fse.IP = tmp.IP
	fse.Port = tmp.Port
	fse.RedundantIP = tmp.RedundantIP
	fse.RedundantPort = tmp.RedundantPort
	fse.RedundantParam1 = tmp.RedundantParam1
	fse.RedundantParam2 = tmp.RedundantParam2
	fse.RedundantParam3 = tmp.RedundantParam3
	fse.IntEndian = tmp.IntEndian
	fse.FloatEndian = tmp.FloatEndian
	fse.Stage = tmp.Stage
	// 更新模块信息
	fse.FSModule = tmp.FSModule
	// 更新变量信息
	err = fse.UpdateVariablesMap()
	if err != nil {
		j_log.LogError([]interface{}{"fse更新变量map失败", fse.Name})
	}
	fse.CalculateDataScale()
	return
}
